﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using GNF.Geo;

namespace GNF.GNFService.GNFServer
{
    /// <summary>
    /// holds a server that publish GPS Data to every client that subscribe to it
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class GNFServiceServer : IGPSDataContract,IDisposable
    {
        /// <summary>
        /// holds a list of all the clients
        /// </summary>
        private static List<IGPSDataCallback> subscribers = new List<IGPSDataCallback>();

        /// <summary>
        /// holds the service host that publish the data
        /// </summary>
        public ServiceHost host = null;

        /// <summary>
        /// open a new server
        /// </summary>
        public void Connect()
        {
            host = new ServiceHost(
                typeof(GNFServiceServer),
                new Uri("net.tcp://localhost:8000"));
            
                //notice the NetTcpBinding?  This allows programs instead of web stuff
                // to communicate with each other
                host.AddServiceEndpoint(typeof(IGPSDataContract),
                  new NetTcpBinding(),
                  "ISubscribe");

                try
                {
                    host.Open();
                    Console.WriteLine("Successfully opened port 8000.");
                }
                catch (Exception e)
                {

                    Console.WriteLine(e.Message);
                }

            
        }

        #region public properties

        /// <summary>
        /// return true of any is subscribed at the momemnt to the service
        /// </summary>
        public bool HasSubscribers { get { return subscribers.Count > 0; } }

        #endregion

        #region IGPSDataContract Members

        /// <summary>
        /// get the callback from the client and add it to the subscribes list
        /// </summary>
        /// <returns>true if subscribed successfully</returns>
        public bool Subscribe()
        {
            try
            {
                //Get the hashCode of the connecting app and store it as a connection
                IGPSDataCallback callback = OperationContext.Current.GetCallbackChannel<IGPSDataCallback>();
                if (!subscribers.Contains(callback))
                    subscribers.Add(callback);
                Console.WriteLine("New Connection: " + callback.GetHashCode());
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
        }

        /// <summary>
        /// removes the client from the client list
        /// </summary>
        /// <returns>true if unsubscribe successfully</returns>
        public bool Unsubscribe()
        {
            try
            {
                //remove any connection that is leaving
                IGPSDataCallback callback = OperationContext.Current.GetCallbackChannel<IGPSDataCallback>();
                if (subscribers.Contains(callback))
                    subscribers.Remove(callback);
                Console.WriteLine("Connection Closed: " + callback.GetHashCode());
                return true;
            }
            catch
            {
                return false;
            }
        }

        #endregion

        /// <summary>
        /// publish the new GPS Data to all subscribed clients
        /// </summary>
        /// <param name="data">the data to publish</param>
        public void PublishGPSData(GPSData data)
        {
            //Go through the list of connections and call their callback funciton
            subscribers.ForEach(delegate(IGPSDataCallback callback)
            {
                if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                {
                    try
                    {
                        Console.WriteLine("Calling PublishGPSData on callback ({0}).", callback.GetHashCode());
                        callback.OnDataChanged(data, DateTime.Now);
                    }
                    catch (Exception)
                    {
                        subscribers.Remove(callback);
                    }
                }
                else
                {
                    subscribers.Remove(callback);
                }
            });

        }

        /// <summary>
        /// close the host
        /// </summary>
        public void CloseConnection()
        {
            try
            {
                if (host != null)
                {
                    host.Close();
                }
            }
            catch (Exception)
            {
            }
        }

        #region IDisposable Members

        /// <summary>
        /// calls CloseConnection thus closing the host
        /// </summary>
        public void Dispose()
        {
            CloseConnection(); 
        }

        #endregion
    }
}
